package com.hantanger.weblog.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.hantanger.weblog.dao.ArchiveDao;
import com.hantanger.weblog.dao.CategoryDao;
import com.hantanger.weblog.dao.CommentDao;
import com.hantanger.weblog.dao.PostCountDao;
import com.hantanger.weblog.dao.PostDao;
import com.hantanger.weblog.dao.SettingDao;
import com.hantanger.weblog.dao.TagDao;
import com.hantanger.weblog.model.Archive;
import com.hantanger.weblog.model.Category;
import com.hantanger.weblog.model.Comment;
import com.hantanger.weblog.model.Post;
import com.hantanger.weblog.model.PostCount;
import com.hantanger.weblog.model.Tag;
import com.hantanger.weblog.util.ClassFactory;

public class PostService {

	public PostDao getPostDao() {
		return ClassFactory.getInstance(PostDao.class);
	}

	public ArchiveDao getArchiveDao() {
		return ClassFactory.getInstance(ArchiveDao.class);
	}

	public CategoryDao getCategoryDao() {
		return ClassFactory.getInstance(CategoryDao.class);
	}

	public CommentDao getCommentDao() {
		return ClassFactory.getInstance(CommentDao.class);
	}

	public PostCountDao getPostCountDao() {
		return ClassFactory.getInstance(PostCountDao.class);
	}

	public TagDao getTagDao() {
		return ClassFactory.getInstance(TagDao.class);
	}

	public SettingDao getSettingDao() {
		return ClassFactory.getInstance(SettingDao.class);
	}

	public Post insert(Post newPost) {
		long now = System.currentTimeMillis();
		newPost.setCreateTime(now);
		newPost.setPublishTime(now);
		return update(newPost);
	}
	
	public Comment addComment(Comment comment){
		getCommentDao().addComment(comment);
		PostCount count = getPostCountDao().getPostCountById(comment.getPostId());
		if(count != null){
			count.setCommentCount(count.getCommentCount() + 1);
			getPostCountDao().update(count);
		}
		return comment;
	}
	
	public List<Comment> getParentCommentList(long postId, int limit, long publishTime){
		return getCommentDao().getParentCommentList(postId, limit, publishTime);
	}
	
	public Map<Long,List<Comment>> getSubCommentList(long[] parentCommentIds, int limit, long publishTime){
		Map<Long, List<Comment>> comments = new HashMap<Long, List<Comment>>();
		for(long parentId : parentCommentIds){
			comments.put(parentId, getCommentDao().getSubCommentList(parentId, limit, publishTime));
		}
		return comments;
	}
	
	public Comment getCommentById(long id){
		return getCommentDao().getCommentById(id);
	}
	
	public Post update(Post newPost) {
		Post oldPost = null;
		if (newPost.getId() != null && newPost.getId() > 0) {//更新日志
			oldPost = getPostById(newPost.getId(), false);
			if(oldPost != null && !oldPost.isPublished() && newPost.isPublished()){
				newPost.setPublishTime(System.currentTimeMillis());
			}
		}
		newPost.setArchiveDate(genArchiveDate(newPost.getPublishTime()));
		getPostDao().update(newPost);
		PostCount postCount = getPostCountDao().getPostCountById(newPost.getId());
		if(postCount == null){
			postCount = new PostCount();
			postCount.setPostId(newPost.getId());
			getPostCountDao().update(postCount);
		}
		updatePostCount(newPost, oldPost);
		return newPost;
	}

	public boolean delete(long postId) {
		Post oldPost = getPostById(postId, false);
		if (oldPost != null) {
			getPostDao().delete(postId);
			updatePostCount(null, oldPost);
		}
		return true;
	}

	/**
	 * 更新日志计数
	 * 
	 * @param oldPost
	 * @param newPost
	 */
	private void updatePostCount(Post oldPost, Post newPost) {
		// '更新各种计数，包括BlogClass, Archive,Tag, 新旧分开处理'
		updateCategoryArchiveCount(oldPost, newPost);
		updateTagCount(oldPost, newPost);
	}

	/**
	 * 日志分类和日期归档计数
	 * 
	 * @param newBlog
	 * @param oldBlog
	 */
	private void updateCategoryArchiveCount(Post newPost, Post oldPost) {
		if (newPost != null) {
			Category newCategory = null;
			if(newPost.getCategoryId() > 0){
				newCategory = getCategoryDao().getCategoryById(newPost.getCategoryId());
			}
			
			if (newCategory == null) {
				newCategory = getCategoryDao().getDefCategory();
			}
			Archive newArchive = getArchiveDao().getArchiveById(newPost.getArchiveDate());
			if (newArchive == null) {
				newArchive = new Archive();
				newArchive.setId(newPost.getArchiveDate());
			}

			newCategory.setAllCount(newCategory.getAllCount() + 1);
			newArchive.setAllCount(newArchive.getAllCount() + 1);
			if (newPost.isPubliced() && newPost.isPublished()) {
				newCategory.setPublicCount(newCategory.getPublicCount() + 1);
				newArchive.setPublicCount(newArchive.getPublicCount() + 1);
			}
			if (newPost.isPublished()) {
				newCategory.setPublishedCount(newCategory.getPublishedCount() + 1);
				newArchive.setPublishedCount(newArchive.getPublishedCount() + 1);
			}
			getArchiveDao().update(newArchive);
			getCategoryDao().update(newCategory);
			if(newPost.getCategoryId() <= 0){
				newPost.setCategoryId(newCategory.getId());
				getPostDao().update(newPost);
			}
		}
		if (oldPost != null) {
			Category category = getCategoryDao().getCategoryById(oldPost.getCategoryId());
			if (category != null) {
				category.setAllCount(category.getAllCount() - 1);
				if (oldPost.isPubliced() && oldPost.isPublished()){
					category.setPublicCount(category.getPublicCount() - 1);
				}
				if (oldPost.isPublished()){
					category.setPublishedCount(category.getPublishedCount() - 1);
				}
				getCategoryDao().update(category);
			}

			Archive oldArchive = getArchiveDao().getArchiveById(oldPost.getArchiveDate());
			if (oldArchive != null) {
				oldArchive.setAllCount(oldArchive.getAllCount() - 1);
				if (oldPost.isPubliced() && oldPost.isPublished()){
					oldArchive.setPublicCount(oldArchive.getPublicCount() - 1);
				}
				if (oldPost.isPublished()){
					oldArchive.setPublishedCount(oldArchive.getPublishedCount() - 1);
				}
				getArchiveDao().update(oldArchive);
			}
		}
	}

	/**
	 * 日志标签计数
	 * 
	 * @param newBlog
	 * @param oldBlog
	 */
	private void updateTagCount(Post newPost, Post oldPost) {
		Map<String, String> oldTagNameMap = new HashMap<String, String>();
		Map<String, String> newTagNameMap = new HashMap<String, String>();

		if (oldPost != null && oldPost.getTags() != null) {
			for (String tag : oldPost.getTags()) {
				if(StringUtils.isNotBlank(tag))
					oldTagNameMap.put(tag, tag);
			}
		}
		if (newPost != null && newPost.getTags() != null) {
			for (String tag : newPost.getTags()) {
				if(StringUtils.isNotBlank(tag))
					newTagNameMap.put(tag, tag);
			}
		}

		if (oldPost != null && oldPost.getTags() != null) {
			for (String tagName : oldPost.getTags()) {// 删除的标签
				if (StringUtils.isNotBlank(tagName) && newTagNameMap.get(tagName) == null) {
					Tag tag = getTagDao().getTagByName(tagName);
					if (tag == null)
						continue;
					tag.setAllCount(tag.getAllCount() - 1);
					if (oldPost.isPubliced() && oldPost.isPublished()){
						tag.setPublicCount(tag.getPublicCount() - 1);
					}
					if (oldPost.isPublished()){
						tag.setPublishedCount(tag.getPublishedCount() - 1);
					}
					getTagDao().update(tag);
				}
			}
		}

		if (newPost != null && newPost.getTags() != null) {
			for (String tagName : newPost.getTags()) {// 新增的标签
				if (StringUtils.isNotBlank(tagName) && oldTagNameMap.get(tagName) == null) {
					Tag tag = getTagDao().getTagByName(tagName);
					if (tag == null) {
						tag = new Tag();
						tag.setName(tagName);
					}
					tag.setAllCount(tag.getAllCount() + 1);
					if (newPost.isPubliced() && newPost.isPublished()){
						tag.setPublicCount(tag.getPublicCount() + 1);
					}
					if (newPost.isPublished()){
						tag.setPublishedCount(tag.getPublishedCount() + 1);
					}
					getTagDao().update(tag);
				}
			}
		}

	}
	
	/**
	 * 根据时间生成只包括年月日的时间
	 * @param dateTime
	 * @return
	 */
	private long genArchiveDate(long time) {
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(time);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTimeInMillis();
	}

	public Post getPostById(Long postId, boolean incViewCount) {
		Post post = getPostDao().getPostById(postId);
		if(post == null){
			return post;
		}
		PostCount count = getPostCountDao().getPostCountById(postId);
		if(count != null){
			if(incViewCount){
				count.setViewCount(count.getViewCount() + 1);
				getPostCountDao().update(count);
			}
			post.setPostCount(count);
		}
		fillPost(post);
		return post;
	}

	public List<Post> getPostsByCategory(long categoryId, int limit,
			long publishTime, boolean publicOnly) {
		List<Post> posts = getPostDao().getPostsByCategory(categoryId, limit, publishTime, publicOnly);
		fillPost(posts);
		return posts;
	}

	public List<Post> getPostsByArchive(long archiveId, int limit, long publishTime,
			boolean publicOnly) {
		List<Post> posts = getPostDao().getPostsByArchive(archiveId, limit, publishTime, publicOnly);
		fillPost(posts);
		return posts;
	}

	public List<Post> getPostsByTagName(String tagName, int limit, long publishTime,
			boolean publicOnly) {
		List<Post> posts = getPostDao().getPostsByTagName(tagName, limit, publishTime, publicOnly);
		fillPost(posts);
		return posts;
	}
	
	public List<Post> getPosts(int limit, long publishTime, boolean publicOnly) {
		List<Post> posts = getPostDao().getPosts(limit, publishTime, publicOnly);
		fillPost(posts);
		return posts;
	}

	public List<Post> getDraftPosts(int limit, long publishTime) {
		List<Post> posts = getPostDao().getDraftPosts(limit, publishTime);
		fillPost(posts);
		return posts;
	}
	
	/**
	 * 上一篇下一篇日志
	 * @param publishTime
	 * @param publicOnly
	 * @return
	 */
	public List<Post> getPrevNextPosts(long publishTime, boolean publicOnly){
		return getPostDao().getPrevNextPosts(publishTime, publicOnly);
	}
	
	public List<Category> getAllCategorys() {
		return getCategoryDao().getAllCategorys();
	}
	
	private void fillPost(Post... posts){
		if(posts == null){
			return;
		}
		fillPost(Arrays.asList(posts));
	}
	
	private void fillPost(List<Post> posts){
		if(posts == null){
			return;
		}
		List<Category> categorys = getAllCategorys();
		Map<Long, Category> categoryMap = new HashMap<Long, Category>();
		
		Category defCategory = getCategoryDao().getDefCategory();
		
		for(Category category : categorys){
			categoryMap.put(category.getId(), category);
		}
		
		List<Long> postIds = new ArrayList<Long>();
		for(Post post : posts){
			postIds.add(post.getId());
		}
		Map<Long, PostCount> postCountMap = getPostCountDao().getPostCountMap(postIds);
		
		for(Post post : posts){
			if(post == null){
				continue;
			}
			Category category = categoryMap.get(post.getCategoryId());
			if(category == null){
				category = defCategory;
			}
			post.setCategory(category);
			
			PostCount postCount = postCountMap.get(post.getId());
			if(postCount == null){
				postCount = new PostCount();
			}
			post.setPostCount(postCount);
		}
	}

	public List<Archive> getAllArchives() {
		return getArchiveDao().getAllArchives();
	}
	
	public List<Tag> getAllTags() {
		return getTagDao().getAllTags();
	}
	
	public Category addCategory(String className, long parentClassId){
		Category category = new Category();
		category.setName(className);
		category.setParentId(parentClassId);
		getCategoryDao().insert(category);
		return category;
	}

}
